package com.hnx.admin.common.security.utils;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hnx.admin.api.security.api.CaptchaVerify;
import com.hnx.admin.api.security.core.SecurityPropertyAPI;
import com.hnx.admin.common.redis.core.RedisCore;
import com.hnx.admin.common.security.core.SecurityProperties;
import com.hnx.admin.common.security.exception.AuthenticationVerifyException;
import com.hnx.admin.common.security.exception.RequestEmptyException;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.MediaType;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/*
创建人： 倔强的头发
创建时间：2024/7/10
功能描述:

*/
public class RequestParamsWapperUtil implements SecurityPropertyAPI {
    public static Map<String,Object> requestParam(HttpServletRequest request, SecurityProperties securityProperties) throws IOException {
        String id = RequestContextHolderUtil.getRequestId();
        if(StrUtil.isNotBlank(id) && id.trim().length()>0){
            return RequestContextHolderUtil.getContext();
        }
        else {
            String reqKey = RequestContextHolderUtil.generateRequestId();
            RequestContextHolderUtil.setRequestId(reqKey);
            Map<String,Object> loginQuery = getLoginParam(request);
            RequestContextHolderUtil.setContext(loginQuery);
            verifyEmpty(loginQuery,securityProperties);
            CaptchaVerify bean = SpringUtil.getBean(CaptchaVerify.class);
            Optional.ofNullable(bean).orElseThrow(() -> new AuthenticationVerifyException(NO_CAPTCHA_API));
            bean.verify(loginQuery);
            return loginQuery;
        }
    }
    private static void verifyEmpty(Map<String,Object> loginQuery, SecurityProperties securityProperties){
        String username = RequestContextHolderUtil.extractUsername(loginQuery,securityProperties.getUsername());
        SecurityVerifyUtil.isTrue(StrUtil.isNotEmpty(username),USER_EMPTY_ERROR);
        RedisCore redisCore = SpringUtil.getBean(RedisCore.class);
        Integer cacheObject = redisCore.getCacheObject(PWD_ERR_CNT_KEY + username, Integer.class);
        SecurityVerifyUtil.isTrue(Optional.ofNullable(cacheObject).orElse(0) < securityProperties.getMaxRetryCount(),RETRY_ERROR_COUNT);
        SecurityVerifyUtil.isTrue(ObjectUtil.isNotEmpty(loginQuery.get(securityProperties.getPassword())),PASSWORD_EMPTY_ERROR);
        SecurityVerifyUtil.isTrue(ObjectUtil.isNotEmpty(loginQuery.get(securityProperties.getUuid())),LOGIN_UUID_NONE);
    }
    public static Map<String,Object> getLoginParam(HttpServletRequest request) throws IOException {
        Map<String, Object> loginQuery;
        if (isJsonRequest(request)) {
            loginQuery = parseJsonRequest(request);
        } else {
            Map<String, String[]> parameterMap = request.getParameterMap();
            // 优化点：检查参数map是否为空，而不是直接通过Optional抛出异常
            if (parameterMap.isEmpty()) {
                throw new RequestEmptyException(LOGIN_REQUEST_EMPTY);
            }
            loginQuery = convertMap(parameterMap);
        }
        return loginQuery;
    }
    private static boolean isJsonRequest(HttpServletRequest request) {
        String contentType = request.getContentType();
        return contentType.equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE) || contentType.equalsIgnoreCase(MediaType.APPLICATION_JSON_UTF8_VALUE);
    }
    /**
     * 解析JSON格式的HTTP请求体。
     *
     * 该将HTTP请求体中的JSON数据转换为Map对象，以便于后续处理。使用Jackson库的ObjectMapper类来处理JSON解析。
     *
     * @param request HTTP请求对象，从中读取请求体。
     * @return 包含解析后JSON数据的Map对象。
     * @throws IOException 解析JSON过程中发生错误。
     */
    private static Map<String, Object> parseJsonRequest(HttpServletRequest request) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        JsonNode rootNode = objectMapper.readTree(request.getInputStream());
        return objectMapper.convertValue(rootNode, Map.class);
    }
    /**
     * 将包含字符串数组的映射转换为包含单个字符串的映射。
     *
     * @param arrayMap 原始映射，其中值是字符串数组。
     * @return 转换后的映射，其中值是字符串数组的第一个元素。
     */
    private static Map<String, Object> convertMap(Map<String, String[]> arrayMap) {
        Map<String, Object> stringMap = new HashMap<>();
        for (Map.Entry<String, String[]> entry : arrayMap.entrySet()) {
            stringMap.put(entry.getKey(), entry.getValue()[0]);
        }
        return stringMap;
    }
}
